package edu.hawaii.wattwaiter.wattdepotcommands;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Calculates where to place the color flags that are assigned to
 * times by how much carbon is being emitted.  A wrapper class for 
 * <code>CarbonEmittedCollector</code> made for ease of use by the WattWaiter system.<br />
 * The highest times are given red flags.  After all red flags are given, 
 * yellow flags are given for the  next highest times.  Once all the red and
 * yellow flags are given, all other times are given green flags,
 * unless the carbon is 0.0.  Then that time has no data associated with it. <br />
 * <br />
 * Collecting a <code>List</code> of integers that represent the flags from November
 * 1st, 2009:
 * <pre>
 *  CarbonFlagSetter command = new CarbonFlagSetter("2009-11-01");
 *  List&lt;Integer&gt; results = command.getFlags(null);
 * </pre>
 * Collecting a <code>List</code> of integers that represent the flags using a <code>List</code>
 * of doubles already ready:
 * <pre>
 *  List&lt;Double&gt; alreadyReady = ...
 *  ...
 *  CarbonFlagSetter command = new CarbonFlagSetter(null);
 *  List&lt;Integer&gt; results = command.getFlags(alreadyReady);
 * </pre>
 * 
 * @author David Mau
 */
public class CarbonFlagSetter {
  /** What server the client should contact.  If null, uses the default. */
  private static String server = null;
  /** Holds output from <code>CarbonEmittedCollector</code>. */
  List<Double> rawList;
  /** Representation of the red flag. */
  static int redFlag = 0;
  /** Representation of the yellow flag. */
  static int yellowFlag = 1;
  /** Representation of the green flag. */
  static int greenFlag = 2;
  /** Representation of the no data flag. */
  static int noDataFlag = 3;
  /** Proportion of red flags to the total number of flags with data.*/
  static double redFlagProportion = 0.25;
  /** Proportion of yellow flags to the total number of flags with data.*/
  static double yellowFlagProportion = 0.25;
  /** Expresses that a value has already been read. */
  private static double alreadyRead = -1.0;

  /**
   * Obtains data from the server with the given date.  The date
   * must be in YYYY-MM-DD format.  If any errors occur, the resulting list
   * will be Integers that will represent no data found.<br />
   * A null date may be passed if no data should be retrieved from the server.
   * This allows for usage of the <code>getFlags</code> method with a provided
   * <code>List</code>.
   * 
   * @param date The given date to collect data for.  Form of the date should be
   * in YYYY-MM-DD, where Y is for years, M is for months, and D is for days.
   */
  public CarbonFlagSetter( String date ) {
    // Establish which server to connect to.
    CarbonEmittedCollector clientConnection = new CarbonEmittedCollector(server);
    
    // Get the list of Doubles that represent the pounds of carbon emitted during
    // a week ago.  The index represents the time in military time.
    rawList = clientConnection.getList( date );
  }
  
  /**
   * Sets all of the flags in the appropriate places for the <code>List</code> collected 
   * from the server, or in a provided <code>List</code>.
   * 
   * @param givenList A list of doubles that need to have flags assigned to them.  If null, uses
   * the list of doubles obtained from the date given when this object was constructed.
   * @return A List holding all of the flags assigned to each hour.  The indexes align with
   * the time of day in military time.
   */
  public List<Integer> getFlags( List<Double> givenList ) {
    // Total flags to place.
    int totalFlags = 24;
    // Create a list to store the results.
    ArrayList<Integer> outputList = new ArrayList<Integer>();
    
    ArrayList<Double> searchList = new ArrayList<Double>();
    // If no list was provided,
    if ( givenList == null ) {
      // Make a copy of the list that the CarbonFlagSetter provided.
      for ( Double rawItem : rawList ) {
        searchList.add( rawItem );
      }
    }
    // If a good list was provided,
    else if ( givenList.size() == 24 ) {
      // Make a copy of the list so that it is okay to modify rows.
      for ( Double givenItem : givenList ) {
        searchList.add( givenItem );
      }
    }
    // Otherwise, a bad list was provided.
    else {
      // Return a list full of nothing but No Data flags.
      for ( int counter = 0; counter < 24; counter++ ) {
        outputList.add( noDataFlag );
      }
      return outputList;
    }
    
    // Prepare the result list.
    for ( int counter = 0; counter < 24; counter++ ) {
      outputList.add( greenFlag );
    }
    
    // Look for rows with no data first.
    for ( int counter = 0; counter < 24; counter++) {
      if ( searchList.get( counter ) == 0.0 ) {
        // Set the flag to no data in the result list.
        outputList.remove( counter );
        outputList.add( counter, noDataFlag );
        totalFlags--;
        // Change the value in the list to be set to already read.
        searchList.remove( counter );
        searchList.add( counter, alreadyRead );
      }
    }
    
    // Place the red flags.
    // Figure out how many red flags there will be.
    double redFlags = totalFlags * redFlagProportion;
    for ( int flagCounter = 0; flagCounter < redFlags; ) {
      // Get the highest measurement in the list.
      double highest = Collections.max(searchList);
      
      // Go through the list and place flags in the result list
      // where the maximum and the carbon match.
      for ( int counter = 0; counter < 24; counter++ ) {
        // If the carbon matches with the number in the list,
        if ( searchList.get( counter ) == highest ) {
          // Set the flag to red in the result list.
          outputList.remove( counter );
          outputList.add( counter, redFlag );
          flagCounter++;
          // Change the value in the list to be set to already read.
          searchList.remove( counter );
          searchList.add( counter, alreadyRead );
        }
      }
    }
    
    // Place the yellow flags.
    // Figure out how many yellow flags there will be.
    double yellowFlags = totalFlags * yellowFlagProportion;
    for ( int flagCounter = 0; flagCounter < yellowFlags; ) {
      // Get the highest measurement in the list.
      double highest = Collections.max(searchList);
      
      // Go through the list and place flags in the result list
      // where the maximum and the carbon match.
      for ( int counter = 0; counter < 24; counter++ ) {
        // If the carbon matches with the number in the list,
        if ( searchList.get( counter ) == highest ) {
          // Set the flag to red in the result list.
          outputList.remove( counter );
          outputList.add( counter, yellowFlag );
          flagCounter++;
          // Change the value in the list to be set to already read.
          searchList.remove( counter );
          searchList.add( counter, alreadyRead );
        }
      }
    }
    
    // Return the list.
    return outputList;
  }

}
